#include "unixHeader.h"
#include "hash.hpp"
#include "token.hpp"
#include <iostream>

#include <workflow/WFFacilities.h>
#include <workflow/MySQLResult.h>
#include <workflow/MySQLMessage.h>
#include <wfrest/HttpServer.h>
//#include <wfrest/json.hpp>
#include <nlohmann/json.hpp>

using namespace std;
using namespace wfrest;

static WFFacilities::WaitGroup waitGroup(1);

void test0()
{
    HttpServer server;

    server.GET("/file/upload", [](const HttpReq *, HttpResp * resp){
        resp->File("./static/view/index.html");
    });

    server.GET("/file/upload/success", [](const HttpReq *, HttpResp * resp){
        resp->String("file upload success");
    });

    server.GET("/static/view/signin.html", [](const HttpReq *, HttpResp * resp){
        resp->File("./static/view/signin.html");
    });

    server.GET("/user/signup", [](const HttpReq *, HttpResp * resp){
        resp->File("./static/view/signup.html");
    });

    server.GET("/static/view/home.html", [](const HttpReq *, HttpResp * resp){
        resp->File("./static/view/home.html");
    });

    server.GET("/static/img/avatar.jpeg", [](const HttpReq *, HttpResp * resp){
        resp->File("./static/img/avatar.jpeg");
    });

    server.GET("/static/js/auth.js", [](const HttpReq *, HttpResp * resp){
        resp->File("./static/js/auth.js");
    });

    server.POST("/user/signup", [](const HttpReq *req, HttpResp * resp, SeriesWork * series){
        //1. 解析请求
        auto formKV = req->form_kv();
        string username = formKV["username"];
        string password = formKV["password"];
        //2. 加密用户密码
        string salt("12345678");
        string encodedpasswd(crypt(password.c_str(), salt.c_str()));
        cout << "encoded passwd:" << encodedpasswd << endl;
        //3. 更新数据库
        string mysqlurl("mysql://root:1234@localhost");
        auto mysqlTask = WFTaskFactory::create_mysql_task(mysqlurl, 1, 
            [resp](WFMySQLTask * mysqltask){
                cout << "mysqlCallback is running" << endl;
                //错误的检测
                int state = mysqltask->get_state();
                int error = mysqltask->get_error();
                if(state != WFT_STATE_SUCCESS) {
                    printf("occurs error: %s\n", WFGlobal::get_error_string(state, error));
                    return;
                }
                //语法的检测
                auto mysqlresp = mysqltask->get_resp();
                if(mysqlresp->get_packet_type() == MYSQL_PACKET_ERROR) {
                    printf("ERROR %d, %s\n", mysqlresp->get_error_code(),
                           mysqlresp->get_error_msg().c_str());
                    return;
                }

                protocol::MySQLResultCursor cursor(mysqltask->get_resp());
                if(cursor.get_cursor_status() == MYSQL_STATUS_OK && 
                   cursor.get_affected_rows() == 1) {
                    //完成了写操作
                    resp->String("SUCCESS");
                } else {
                    resp->String("error");
                }
            });
        string sql("INSERT INTO cloudisk.tbl_user(user_name, user_pwd, status) VALUES('");
        sql += username + "','" + encodedpasswd + "', 0)";
        mysqlTask->get_req()->set_query(sql);
        series->push_back(mysqlTask);
    });
    
    server.POST("/user/signin", [](const HttpReq *req, HttpResp * resp, SeriesWork * series){
        //1. 解析请求
        auto formKV = req->form_kv();
        string username = formKV["username"];
        string password = formKV["password"];
        //2. 加密用户密码
        string salt("12345678");
        string encodedpasswd(crypt(password.c_str(), salt.c_str()));
        cout << "encoded passwd:" << encodedpasswd << endl;
        //3. 查询数据库
        string mysqlurl("mysql://root:1234@localhost");
        auto mysqlTask = WFTaskFactory::create_mysql_task(mysqlurl, 1, 
            [resp, encodedpasswd, username, salt, mysqlurl](WFMySQLTask * mysqltask){
                cout << "mysqlCallback is running" << endl;
                //错误的检测
                int state = mysqltask->get_state();
                int error = mysqltask->get_error();
                if(state != WFT_STATE_SUCCESS) {
                    printf("occurs error: %s\n", WFGlobal::get_error_string(state, error));
                    return;
                }
                //语法的检测
                auto mysqlresp = mysqltask->get_resp();
                if(mysqlresp->get_packet_type() == MYSQL_PACKET_ERROR) {
                    printf("ERROR %d, %s\n", mysqlresp->get_error_code(),
                           mysqlresp->get_error_msg().c_str());
                    return;
                }

                using namespace protocol;
                MySQLResultCursor cursor(mysqltask->get_resp());
                vector<vector<MySQLCell>> rows;
                cursor.fetch_all(rows);
                string pwd = rows[0][0].as_string();

                if(pwd == encodedpasswd) {
                    //验证成功,生成Token
                    Token tokenObj(username, salt);
                    string tokenStr = tokenObj.getToken();
                    //将Token添加到tbl_user_token表中
                    auto nextTask = WFTaskFactory::create_mysql_task(mysqlurl, 1, nullptr);
                    string sql("REPLACE INTO cloudisk.tbl_user_token(user_name, user_token) VALUES('");
                    sql += username + "','" + tokenStr + "')";
                    nextTask->get_req()->set_query(sql);
                    series_of(mysqltask)->push_back(nextTask);
                    //生成JSON对象
                    using Json = nlohmann::json;
                    Json msg;
                    Json data;
                    data["Token"] = tokenStr;
                    data["Username"] = username;
                    data["Location"] = "/static/view/home.html";
                    msg["data"] = data;
                    resp->String(msg.dump());
                }
            });
        string sql("select user_pwd from cloudisk.tbl_user where user_name='");
        sql += username + "' limit 1";
        mysqlTask->get_req()->set_query(sql);
        series->push_back(mysqlTask);
    });

    server.POST("/user/info", [](const HttpReq *req, HttpResp * resp, SeriesWork * series){
        //1. 解析请求
        auto queryList = req->query_list();
        string username = queryList["username"];
        string tokenStr = queryList["token"];
        cout << "username:" << username << endl;
        cout << "token:" << tokenStr << endl;
        //2. 校验token 
        //3. 查询数据库表 tbl_user表
        string mysqlurl("mysql://root:1234@localhost");
        auto mysqlTask = WFTaskFactory::create_mysql_task(mysqlurl, 1, 
            [resp, username](WFMySQLTask * mysqltask){
                cout << "mysqlCallback is running" << endl;
                //错误的检测
                int state = mysqltask->get_state();
                int error = mysqltask->get_error();
                if(state != WFT_STATE_SUCCESS) {
                    printf("occurs error: %s\n", WFGlobal::get_error_string(state, error));
                    return;
                }
                //语法的检测
                auto mysqlresp = mysqltask->get_resp();
                if(mysqlresp->get_packet_type() == MYSQL_PACKET_ERROR) {
                    printf("ERROR %d, %s\n", mysqlresp->get_error_code(),
                           mysqlresp->get_error_msg().c_str());
                    return;
                }

                using namespace protocol;
                MySQLResultCursor cursor(mysqltask->get_resp());
                vector<vector<MySQLCell>> rows;
                cursor.fetch_all(rows);
                if(rows[0][0].is_datetime()) {
                    //构造JSON对象，发送给客户端
                    Json msg;
                    Json data;
                    data["Username"] = username;
                    data["SignupAt"] = rows[0][0].as_datetime();
                    msg["data"] = data;
                    resp->String(msg.dump());
                }
            });
        string sql("select signup_at from cloudisk.tbl_user where user_name='");
        sql += username + "'";
        cout << "sql:\n" << sql << endl;
        mysqlTask->get_req()->set_query(sql);
        series->push_back(mysqlTask);
    });

    server.POST("/file/query", [](const HttpReq *req, HttpResp * resp, SeriesWork * series){
        //1. 解析请求
        auto queryList = req->query_list();
        string username = queryList["username"];
        string tokenStr = queryList["token"];
        auto formKV = req->form_kv();
        string limitcnt = formKV["limit"];
        cout << "username:" << username << endl;
        cout << "token:" << tokenStr << endl;
        cout << "limitcnt:" << limitcnt << endl;
        //2. 校验token 
        //3. 查询数据库表 tbl_user_file表
        string mysqlurl("mysql://root:1234@localhost");
        auto mysqlTask = WFTaskFactory::create_mysql_task(mysqlurl, 1, 
            [resp, username](WFMySQLTask * mysqltask){
                cout << "mysqlCallback is running" << endl;
                //错误的检测
                int state = mysqltask->get_state();
                int error = mysqltask->get_error();
                if(state != WFT_STATE_SUCCESS) {
                    printf("occurs error: %s\n", WFGlobal::get_error_string(state, error));
                    return;
                }
                //语法的检测
                auto mysqlresp = mysqltask->get_resp();
                if(mysqlresp->get_packet_type() == MYSQL_PACKET_ERROR) {
                    printf("ERROR %d, %s\n", mysqlresp->get_error_code(),
                           mysqlresp->get_error_msg().c_str());
                    return;
                }

                using namespace protocol;
                MySQLResultCursor cursor(mysqltask->get_resp());
                vector<vector<MySQLCell>> rows;
                cursor.fetch_all(rows);
                if(rows.size() == 0) return;

                //构造JSON对象，发送给客户端
                Json msgArr;
                for(size_t i = 0; i < rows.size(); ++i){
                    Json data;
                    data["FileHash"] = rows[i][0].as_string();
                    data["FileName"] = rows[i][1].as_string();
                    data["FileSize"] = rows[i][2].as_ulonglong();
                    data["UploadAt"] = rows[i][3].as_datetime();
                    data["LastUpdated"] = rows[i][4].as_datetime();
                    msgArr.push_back(data);
                }       
                resp->String(msgArr.dump());
            });
        string sql("select file_sha1, file_name, file_size, upload_at, last_update from cloudisk.tbl_user_file where user_name='");
        sql += username + "' limit " + limitcnt;
        cout << "sql:\n" << sql << endl;
        mysqlTask->get_req()->set_query(sql);
        series->push_back(mysqlTask);
    });

    server.POST("/file/upload", [](const HttpReq *req, HttpResp * resp, SeriesWork * series){
        //1.解析请求
        auto userInfo = req->query_list();
        string username = userInfo["username"];
        if(req->content_type() == MULTIPART_FORM_DATA) {
            //获取文件的信息
            auto formMap = req->form();
            string filename = formMap["file"].first;
            string content = formMap["file"].second;
            size_t filesize = content.size();
            cout << "filename:"  << filename << endl;
            //cout << "filecontent:" << content << endl;

            string filepath = "./tmp/" + filename;
            mkdir("./tmp", 0775);
            //resp->Save(filepath, content);//保存到本地 创建pwrite_task
            //resp->set_status_code("301");
            //resp->headers["location"] = "/file/upload/success";
            int fd = open(filepath.c_str(), O_CREAT|O_RDWR, 0664);
            if(fd < 0) {
                perror("open");
                return;
            }
            write(fd, content.c_str(), filesize);
            close(fd);

            //生成sha1的字符串
            Hash hash(filepath);
            string filehash = hash.sha1();
            //更新数据库
            string mysqlurl("mysql://root:1234@localhost");
            auto mysqlTask = WFTaskFactory::create_mysql_task(mysqlurl, 1, 
            [resp](WFMySQLTask * mysqltask){
                cout << "mysqlCallback is running" << endl;
                //错误的检测
                int state = mysqltask->get_state();
                int error = mysqltask->get_error();
                if(state != WFT_STATE_SUCCESS) {
                    printf("occurs error: %s\n", WFGlobal::get_error_string(state, error));
                    return;
                }
                //语法的检测
                auto mysqlresp = mysqltask->get_resp();
                if(mysqlresp->get_packet_type() == MYSQL_PACKET_ERROR) {
                    printf("ERROR %d, %s\n", mysqlresp->get_error_code(),
                           mysqlresp->get_error_msg().c_str());
                    return;
                }

                protocol::MySQLResultCursor cursor(mysqltask->get_resp());
                if(cursor.get_cursor_status() == MYSQL_STATUS_OK && 
                   cursor.get_affected_rows() == 1) {
                    //完成了写操作
                    resp->set_status_code("301");
                    resp->headers["location"] = "/file/upload/success";
                }
            });
            string sql = "INSERT INTO cloudisk.tbl_file(file_sha1, file_name, file_size, file_addr, status) VALUES(";
            sql += "'" + filehash + "','" + filename + "', " + std::to_string(filesize) + ", '" + filepath + "', 0);";

            sql += "INSERT INTO cloudisk.tbl_user_file(user_name, file_sha1, file_size, file_name, status) VALUES(";
            sql += "'" + username + "','" + filehash + "'," + std::to_string(filesize) + ",'" + filename + "', 0);";
            cout << "sql:\n" << sql << endl;
            mysqlTask->get_req()->set_query(sql);
            series->push_back(mysqlTask);
        }
    });

    server.POST("/file/downloadurl", [](const HttpReq *req, HttpResp * resp){
        //1.解析请求
        auto queryList = req->query_list();
        string filename = queryList["filename"];
        cout << "filename:" << filename << endl;

        string downloadurl = "http://192.168.30.128:8080/" + filename;
        resp->String(downloadurl);
    });

    server.GET("/file/download", [](const HttpReq *req, HttpResp * resp){
        //1. 解析请求
        auto queryList = req->query_list();
        string filename = queryList["filename"];
        string filesizeStr = queryList["filesize"];
        string filehash = queryList["filehash"];
        cout << "filename:" << filename << endl;

        //下载方案二:
        resp->set_status_code("301");
        resp->headers["Location"] = "http://192.168.30.128:8080/" + filename;

#if 0
        //下载方案一：
        //2. 读取文件
        string filepath = "./tmp/" + filename;
        int fd = open(filepath.c_str(), O_RDONLY);
        if(fd < 0) {
            perror("open");
            return;
        }
        
        size_t filesize = std::atoi(filesizeStr.c_str());
        char * pbuff = new char[filesize + 1]();
        read(fd, pbuff, filesize);
        resp->append_output_body(pbuff, filesize);
        resp->headers["Content-Type"] = "application/octet-stream";
        resp->headers["Content-Disposition"] = "attachment;filename=" + filename;
        delete [] pbuff;
#endif
    });


    if(server.track().start(8888) == 0) {
        server.list_routes();
        waitGroup.wait();
        server.stop();
    } else {
        printf("server cannnot start\n");
    }
}


int main()
{
    test0();
    return 0;
}

